﻿Imports SharedTools
Imports System.Xml
Imports System.Security.Cryptography
Imports Org.BouncyCastle.Security
Imports Org.BouncyCastle.Crypto.Parameters
Imports System.Security.Cryptography.Xml
Imports Org.BouncyCastle.Pkcs
Imports Org.BouncyCastle.X509

Public Class CertificateRequestManager
    Public Shared Function RenewCertificates(r As String) As String
        Dim username = XmlTool.GetValue("Username", r)
        Dim currentCryptoCert = CertificatesManager.GetCurrentCertificateByUsername(username, CertificateType.Cryptography)
        Dim currentSigCert = CertificatesManager.GetCurrentCertificateByUsername(username, CertificateType.Signature)

        If currentSigCert Is Nothing Then
            Dim resp = XmlTool.GenerateXML("RenewalRequestResponse", "Response", GenericQueryResponse.ClientUnknownException.ToString)
            Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
        End If

        Dim pubKey = DirectCast(currentSigCert.Certificate.GetPublicKey, RsaKeyParameters)
        Dim t As CertificateType = If(XmlTool.IsElementPresent("NewSigRequest", r), CertificateType.Signature, CertificateType.Cryptography)
        'Dim revokeBoth As Boolean = Boolean.Parse(XmlTool.GetAttributeValue("RenewalRequest", "RenewBoth", r))

        If XmlTool.CheckSignature(r, pubKey) Then
            Dim resp As String = XmlTool.GenerateXML("RenewalRequestResponse")
            If currentCryptoCert IsNot Nothing AndAlso t = CertificateType.Cryptography Then
                'crypto
                Dim cryptoRequestBytes = Convert.FromBase64String(XmlTool.GetValue("NewCryptoRequest", r))
                Dim cryptoRequest As New Pkcs10CertificationRequest(cryptoRequestBytes)
                Dim cryptoRequestItem As New CertificateRequestItem(cryptoRequest, username, IDgenerator.ID) 'TODO: indagare se va bene fare cosi
                cryptoRequestItem.Status = CertificateRequestStatus.Renewed
                Repository.Requests.Add(cryptoRequestItem)
                Dim newCryptoCert = CertificatesManager.CreateCertificateFromRequest(cryptoRequestItem)
                Dim newCryptoCertItem = New CertificateItem(newCryptoCert, username)
                Repository.Certificates.Add(newCryptoCertItem)
                CRLManager.UpdateCRL(currentCryptoCert.Certificate.SerialNumber)
                currentCryptoCert.Refresh()

                resp = XmlTool.AddElement(resp, "RenewalRequestResponse", "NewCryptoCertificate", Convert.ToBase64String(newCryptoCert.GetEncoded))
            End If

            If t = CertificateType.Signature Then
                'signature
                Dim sigRequestBytes = Convert.FromBase64String(XmlTool.GetValue("NewSigRequest", r))
                Dim sigRequest As New Pkcs10CertificationRequest(sigRequestBytes)
                Dim sigRequestItem As New CertificateRequestItem(sigRequest, username, IDgenerator.ID) 'TODO: indagare se va bene fare cosi
                sigRequestItem.Status = CertificateRequestStatus.Renewed
                Repository.Requests.Add(sigRequestItem)
                Dim newSigCert = CertificatesManager.CreateCertificateFromRequest(sigRequestItem)
                Dim newSigCertItem = New CertificateItem(newSigCert, username)
                Repository.Certificates.Add(newSigCertItem)
                CRLManager.UpdateCRL(currentSigCert.Certificate.SerialNumber)
                currentSigCert.Refresh()

                resp = XmlTool.AddElement(resp, "RenewalRequestResponse", "NewSigCertificate", Convert.ToBase64String(newSigCert.GetEncoded))
            End If

            PersistenceManager.SaveRequests()
            PersistenceManager.SaveCertificates()
            PersistenceManager.SaveCRL()

            resp = XmlTool.AddAttribute(resp, "RenewalRequestResponse", "Response", GenericQueryResponse.Ok.ToString)
            Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
        Else
            Dim resp = XmlTool.GenerateXML("RenewalRequestResponse", "Response", GenericQueryResponse.SignatureException.ToString)
            Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
        End If

    End Function

    Public Shared Function ProcessCertificateRequest(r As String) As String

        Dim username = XmlTool.GetValue("Username", r)
        Dim requestBytes = Convert.FromBase64String(XmlTool.GetValue("PKCS10", r))
        Dim request As New Pkcs10CertificationRequest(requestBytes)

        Dim pubKey = DirectCast(request.GetPublicKey, RsaKeyParameters)
        Dim signingKey As RsaKeyParameters
        Dim cri As New CertificateRequestItem(request, username, -1)
        If cri.RequestType = CertificateType.Signature Then
            signingKey = pubKey
        Else
            Try
                Dim sigCert = CertificatesManager.GetCurrentCertificateByUsername(username, CertificateType.Signature).Certificate
                If sigCert.NotAfter < DateTime.Now Then
                    'expired
                    Dim resp = XmlTool.GenerateXML("CertificateRequestResponse", "Response", GenericQueryResponse.ClientCertificateExpiredException.ToString, -1)
                    Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
                End If

                signingKey = DirectCast(sigcert.GetPublicKey, RsaKeyParameters)
            Catch ex As Exception
                Dim resp = XmlTool.GenerateXML("CertificateRequestResponse", "Response", GenericQueryResponse.ClientUnknownException.ToString, -1)
                Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
            End Try
        End If

        If XmlTool.CheckSignature(r, signingKey) Then
            Dim id = IDgenerator.ID
            cri.ID = id
            Repository.Requests.Add(cri)
            PersistenceManager.SaveRequests()

            Dim resp = XmlTool.GenerateXML("CertificateRequestResponse", "Response", "Accepted", id)
            Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
        Else
            Dim resp = XmlTool.GenerateXML("CertificateRequestResponse", "Response", "SignatureVerificationFailed", -1)
            Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
        End If
    End Function

    Public Shared Function IsCertificateReady(q As String) As String
        Dim x As New XmlDocument
        x.LoadXml(q)

        Dim id = CInt(x.GetElementsByTagName("ID")(0).InnerText)

        Dim req = (From c In Repository.Requests Where c.ID = id)(0)

        Dim pubKey = DirectCast(req.Request.GetPublicKey, RsaKeyParameters)
        Dim signingKey As RsaKeyParameters
        If req.RequestType = CertificateType.Signature Then
            signingKey = pubKey
        Else
            Try
                signingKey = DirectCast(CertificatesManager.GetCurrentCertificateByUsername(req.UserName, CertificateType.Signature).Certificate.GetPublicKey, RsaKeyParameters)
            Catch ex As Exception
                Dim resp = XmlTool.GenerateXML("CertificateRequestResponse", "Response", CertificateRequestStatus.ClientUnknownException.ToString, -1)
                Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
            End Try
        End If
        Dim xml As String = String.Empty

        Select Case req.Status
            Case CertificateRequestStatus.Accepted
                Dim candidates = (From c In Repository.Certificates Where c.ID = id).ToList
                Dim cert = candidates(0)
                If XmlTool.CheckSignature(q, signingKey) Then
                    xml = XmlTool.GenerateXML("IsCertificateReadyResponse", id)
                    xml = XmlTool.AddAttribute(xml, "IsCertificateReadyResponse", "Response", CertificateRequestStatus.Accepted.ToString)
                    xml = XmlTool.AddElement(xml, "IsCertificateReadyResponse", "Certificate", Convert.ToBase64String(cert.Certificate.GetEncoded))

                    req.Status = CertificateRequestStatus.Fulfilled
                    PersistenceManager.SaveRequests()
                Else
                    'Failed
                    xml = XmlTool.GenerateXML("IsCertificateReadyResponse", "Exception", "Signature verification failed. You are not who you claim to be!", id)
                    xml = XmlTool.AddAttribute(xml, "IsCertificateReadyResponse", "Response", CertificateRequestStatus.SignatureException.ToString)
                End If
            Case CertificateRequestStatus.Fulfilled, CertificateRequestStatus.Refused, CertificateRequestStatus.Pending
                xml = XmlTool.GenerateXML("IsCertificateReadyResponse", id)
                xml = XmlTool.AddAttribute(xml, "IsCertificateReadyResponse", "Response", req.Status.ToString)
        End Select

        Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, xml)

    End Function


    Public Shared Sub ConfirmRequest(req As CertificateRequestItem)
        Dim cert = CertificatesManager.CreateCertificateFromRequest(req)
        Dim certItem = New CertificateItem(cert, req.UserName)
        Repository.Certificates.Add(certItem)

        req.Status = CertificateRequestStatus.Accepted

        PersistenceManager.SaveRequests()
        PersistenceManager.SaveCertificates()
    End Sub

    Public Shared Sub RejectRequest(req As CertificateRequestItem)
        req.Status = CertificateRequestStatus.Refused
        PersistenceManager.SaveRequests()
    End Sub

End Class
